package app

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
	"go.uber.org/zap"
	"originforce.club/bilin-mock/conf"
	"originforce.club/bilin-mock/internal/dao"
	sqlc "originforce.club/bilin-mock/internal/db/sqlc"
	appMiddleware "originforce.club/bilin-mock/internal/rest/middlewares"
	appRender "originforce.club/bilin-mock/internal/rest/render"
	"originforce.club/bilin-mock/internal/util"
)

type AppRouter struct {
	slog *zap.SugaredLogger
	dao  *db.PgDao
	vars *conf.VarsSection
	r    chi.Router
}

// New initialization
func New(
	slog *zap.SugaredLogger,
	dao *db.PgDao,
	vars *conf.VarsSection,
) (r chi.Router) {
	s := &AppRouter{
		slog: slog.With("module", "AppRouter"),
		dao:  dao,
		vars: vars,
		r:    chi.NewRouter(),
	}
	s.register()
	return s.r
}

func (s *AppRouter) register() {
	s.r.With(appMiddleware.WithPager(s.slog, s.vars)).Get("/", s.listApps)
	s.r.Post("/", s.createApp)
	s.r.Route("/{id}", func(r chi.Router) {
		r.Use(s.handleCtx)
		r.Get("/", s.getApp)
		r.Put("/", s.updateApp)
		r.Put("/unactive", s.unactiveApp)
		r.Put("/active", s.activeApp)
	})
}
func (s *AppRouter) handleCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		appID := chi.URLParam(r, "id")
		if util.IsEmpty(appID) {
			render.Render(w, r, appRender.ErrInvalidRequest(fmt.Errorf("appID")))
			return
		}
		s.slog.Infof("id=>%s", appID)
		ctx := context.WithValue(r.Context(), "id", appID)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

/// 列出app

func (s *AppRouter) listApps(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)
	apps, err := s.dao.Q.ListApps(ctx, sqlc.ListAppsParams{
		Status: 1,
		Offset: pagerPtr.Offset,
		Limit:  pagerPtr.Limit,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityListToRender(db.MapObjToPtr(apps))); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 新建app
type createAppRequest struct {
	*sqlc.UpsertAppParams
}

func (s *createAppRequest) Bind(r *http.Request) error {
	if s.UpsertAppParams == nil {
		return errors.New("缺少CreateApp字段.")
	}
	return nil
}

func (s *AppRouter) createApp(w http.ResponseWriter, r *http.Request) {
	data := &createAppRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrInvalidRequest(err))
		return
	}
	ctx := r.Context()
	var appPtr *sqlc.App
	err := s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
		data.ID = fmt.Sprintf("bma%s", util.RandomString(27))
		app, err := q.UpsertApp(ctx, *data.UpsertAppParams)
		if err != nil {
			return
		}
		appPtr = db.ConvObjToPtr(app)
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(appPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 获取app

func (s *AppRouter) getApp(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	app, err := s.dao.Q.GetApp(ctx, ctx.Value("id").(string))
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	appPtr := db.ConvObjToPtr(app)
	if err := render.Render(w, r, appRender.EntityToRender(appPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 更新app
type updateAppRequest struct {
	*sqlc.UpdateAppParams
}

func (s *updateAppRequest) Bind(r *http.Request) error {
	if s.UpdateAppParams == nil {
		return errors.New("缺少UpdateApp字段.")
	}
	return nil
}

func (s *AppRouter) updateApp(w http.ResponseWriter, r *http.Request) {
	data := &updateAppRequest{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrInvalidRequest(err))
		return
	}
	ctx := r.Context()
	data.UpdateAppParams.ID = ctx.Value("id").(string)
	var appPtr *sqlc.App
	err := s.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
		app, err := q.UpdateApp(ctx, *data.UpdateAppParams)
		if err != nil {
			return
		}
		appPtr = db.ConvObjToPtr(app)
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(appPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 停用app
func (s *AppRouter) unactiveApp(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	err := s.dao.Q.UpdateAppStatus(ctx, sqlc.UpdateAppStatusParams{
		ID: ctx.Value("id").(string),
		Status: 0,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 启用app
func (s *AppRouter) activeApp(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	err := s.dao.Q.UpdateAppStatus(ctx, sqlc.UpdateAppStatusParams{
		ID: ctx.Value("id").(string),
		Status: 1,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.DefaultToRender()); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}